package com.zhangfb95.crosschain.server.infra.config.configuration;

import com.zhangfb95.crosschain.infra.model.Result;
import com.zhangfb95.crosschain.infra.statuscode.StatusCode;
import com.zhangfb95.crosschain.infra.util.json.JsonUtil;
import com.zhangfb95.crosschain.protocol.rpc.RpcAuthHandler;
import com.zhangfb95.crosschain.protocol.rpc.RequestHeaderConstants;
import com.zhangfb95.crosschain.server.infra.model.mapper.entity.User;
import com.zhangfb95.crosschain.server.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;

/**
 * @author zhangfubing
 * @since 2023/12/14
 */
@Slf4j
@Component
public class RpcAuthInterceptor implements HandlerInterceptor {

    private static final long FIVE_MINUTES = 5L * 60L * 1000L;
    @Autowired
    private JsonUtil jsonUtil;
    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String userId = request.getHeader(RequestHeaderConstants.USER_ID);
        String nonce = request.getHeader(RequestHeaderConstants.NONCE);
        String timestamp = request.getHeader(RequestHeaderConstants.TIMESTAMP);
        String authorization = request.getHeader(RequestHeaderConstants.AUTHORIZATION);

        long lUserId;
        try {
            lUserId = Long.parseLong(userId);
        } catch (NumberFormatException e) {
            log.error("auth request, UserId is not a number, input: {}", userId);
            authFail(response);
            return false;
        }

        // 时间戳必须是数字
        long lTimestamp;
        try {
            lTimestamp = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            log.error("auth request, Timestamp is not a number, input: {}", timestamp);
            authFail(response);
            return false;
        }

        // 时间戳必须在约定范围内
        if (Math.abs(System.currentTimeMillis() - lTimestamp) > FIVE_MINUTES) {
            log.error("auth request, Timestamp not valid, input: {}", timestamp);
            authFail(response);
            return false;
        }

        // 用户必须存在
        User user = userService.getUserById(lUserId);
        if (user == null) {
            log.error("auth request, user find, userId: {}", userId);
            authFail(response);
            return false;
        }

        // 认证码必须匹配
        String secret = user.getSecret();
        RpcAuthHandler rpcAuthHandler = new RpcAuthHandler(userId, nonce, timestamp);
        if (!rpcAuthHandler.verify(secret, authorization)) {
            log.error("auth request, Authorization not match, input: {}, expected: {}",
                    authorization, rpcAuthHandler.genAuthorization(secret));
            authFail(response);
            return false;
        }

        return true;
    }

    private void authFail(HttpServletResponse response) throws IOException {
        response.setContentType("application/json;charset=utf8");
        response.getWriter().println(jsonUtil.toJsonQuietly(Result.fail(StatusCode.UN_AUTH_ERROR)));
    }
}
